package graph.net;


import graph.net.commands.CommandName;
import graph.net.commands.CommandProcessor;
import graph.net.commands.InvalidCommandException;
import graph.net.commands.InvalidCommandFormatException;
import graph.net.internal.ServiceLocator;
import graph.net.internal.ServiceName;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;



/*********************************************
 * Creates a worker thread to handle client communication.
 * 
 * @author amahmood
 * 
 ********************************************/
public class ServerSession implements Runnable
{
    private Socket clientSocket;
    private BufferedReader in = null;
    private PrintWriter out = null;
    private boolean debug = true;
    private CommandProcessor commandProcessor = null;
    private boolean keepRunning = true;
    
    private ServiceLocator serviceLocator =null;

    /**********************************************
     * @param clientSocket
     *********************************************/
    public ServerSession(Socket clientSocket)
    {
        this.clientSocket = clientSocket;

        serviceLocator = new ServiceLocator();
        serviceLocator.putService(ServiceName.ServerSession, this);
        
        System.out.println("Established connection with " + clientSocket.getPort());
    }

    /********************************************
     * (non-Javadoc)
     * 
     * @see java.lang.Object#finalize()
     ********************************************/
    @Override
    protected void finalize() throws Throwable
    {
        super.finalize();

        if (in != null)
            in.close();

        if (out != null)
            out.close();

        if (clientSocket != null)
            clientSocket.close();
    }

    /********************************************
     * (non-Javadoc)
     * 
     * @see java.lang.Runnable#run()
     ********************************************/
    public void run()
    {
        String message;

        try
        {
            commandProcessor = new CommandProcessor(this.serviceLocator);
            in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            out = new PrintWriter(clientSocket.getOutputStream(), true);
        }
        catch (IOException e)
        {
            System.out.println("Unable to establish read and write channel");
            System.exit(-1);
        }

        while (keepRunning)
        {
            try
            {
                message = in.readLine();

                if (message == null) { throw new IOException(); }

                System.out.println("Received from " + clientSocket.getPort() + ">" + message);

                String response = commandProcessor.processCommand(message);

                sendResponse(response);

                if (CommandName.bye.label().equals(response))
                    keepRunning = false;
            }
            catch (IOException e)
            {
                System.out.println("Unable to read client message.");
                keepRunning = false;
                break;
            }
            catch (UnsupportedOperationException e)
            {
                System.out.println(e.getMessage());
                sendResponse("This command has not been implemented yet.");
            }
            catch (IllegalArgumentException e)
            {
                System.err.println(e.getMessage());
                sendResponse(e.getMessage());
            }
            catch (InvalidCommandFormatException e)
            {
                System.err.println(e.getMessage());
                sendResponse(e.getMessage());
            }
            catch (InvalidCommandException e)
            {
                System.err.println(e.getMessage());
                sendResponse(e.getMessage());
            }
        }
    }

    /*******************************************
     * @param message
     ********************************************/
    public void sendResponse(String message)
    {
        if (debug)
        {
            System.out.println("Sending to " + clientSocket.getPort() + ">" + message);
        }

        if (clientSocket.isConnected())
            out.println(message);

    }

}
